home *** CD-ROM | disk | FTP | other *** search
/ Pascal Super Library / Pascal Super Library (CW International)(1997).bin / LIBRARY / SHDK_2 / SHUTILPK.PAS < prev    next >
Pascal/Delphi Source File  |  1992-04-30  |  23KB  |  714 lines

  1. {$I SHDEFINE.INC}
  2.  
  3. {$I SHUNITSW.INC}
  4. {$O-}
  5.  
  6. {$D-,L-}
  7.  
  8. unit ShUtilPk;
  9. {
  10.                                 ShUtilPk
  11.  
  12.                              A Utility Unit
  13.  
  14.                                    by
  15.  
  16.                               Bill Madison
  17.  
  18.                    W. G. Madison and Associates, Ltd.
  19.                           13819 Shavano Downs
  20.                             P.O. Box 780956
  21.                        San Antonio, TX 78278-0956
  22.                              (512)492-2777
  23.                              CIS 73240,342
  24.  
  25.                   Copyright 1991 Madison & Associates
  26.                           All Rights Reserved
  27.  
  28.         This file may  be used and distributed  only in accord-
  29.         ance with the provisions described on the title page of
  30.                   the accompanying documentation file
  31.                               SKYHAWK.DOC
  32. }
  33.  
  34. Interface
  35.  
  36. Uses
  37.   TpCrt,
  38.   TpString,
  39.   TpDos,
  40.   Dos;
  41.  
  42. type
  43.   CharSet       = set of char;
  44.   DelimSetType  = set of char;
  45.  
  46. const
  47.   DelimSet  : DelimSetType  = [#0..#32];
  48.  
  49. {*****************************************************************}
  50. { !!!!!!!!!!!!!!!!! NEVER MODIFY THESE VARIABLES !!!!!!!!!!!!!!!!!}
  51. {*****************************************************************}
  52. Var
  53.   StartingMode : Byte;
  54. {Initial video mode of the system (Mono, CO80, BW40, ...)}
  55.  
  56.   StartingAttr : Byte;
  57. {Initial video attribute of the system}
  58.  
  59. {*****************************************************************}
  60. {*****************************************************************}
  61.  
  62. function BetwS(Lower, Item, Upper  : LongInt) : boolean;
  63. {Performs a SIGNED test of the condition that Lower <= Item <= Upper,
  64.  returning TRUE if and only if the condition is met. Lower, Item, and
  65.  Upper can be any combination of 1, 2, and 4-byte entities.}
  66.  
  67. {**********************************************************************}
  68.  
  69. function BetwU(Lower, Item, Upper  : LongInt) : boolean;
  70. {Performs an UNSIGNED test of the condition that Lower <= Item <= Upper,
  71.  returning TRUE if and only if the condition is met. Lower, Item, and
  72.  Upper can be any combination of 1, 2, and 4-byte entities.}
  73.  
  74. {**********************************************************************}
  75.  
  76. Function StarString(Pattern, Target : String) : Boolean;
  77. {This function performs a generalization of the wildcard string
  78.  matching usually performed by DOS. A '*' wild card can be placed
  79.  anywhere within the pattern string, and will represent its usual
  80.  'zero or more of any characters'. Scanning will not be terminated
  81.  at that point, however, but will continue. Thus, '*B*EFG' will match
  82.  'ABCDEFG', but '*B*EGF' will not. Similarly, '*ABC*' will match, but
  83.  '*ABC' will not.}
  84.  
  85. {**********************************************************************}
  86.  
  87. Function WhoAmI : String;
  88. {Returns the fully qualified path to the currently executing file.
  89.  *** DOS 3.x or above, ONLY ***}
  90.  
  91. {**********************************************************************}
  92.  
  93. function SearchEnvironment(Code : String) : String;
  94. {Searches the environment space for "CODE" and returns the corresponding
  95.  string.}
  96.  
  97. {**********************************************************************}
  98.  
  99. Function LoWord(LI : LongInt) : Word;
  100. {Returns the low order word of a LongInt.}
  101.  
  102. {**********************************************************************}
  103.  
  104. Function HiWord(LI : LongInt) : Word;
  105. {Returns the high order word of a LongInt.}
  106.  
  107. {**********************************************************************}
  108.  
  109. Function LI(Ilo, Ihi : Word) : LongInt;
  110. {Converts two Word vbls to a LongInt}
  111.  
  112. {**********************************************************************}
  113.  
  114. Function HEX(A : LongInt) : String;
  115. {Converts a byte vbl into a string correspnoding to the hex value.}
  116. {NOTE: The parameter A may be of any Integer type (ShortInt, Byte,
  117.  Integer, Word, or LongInt}
  118. {HEX will return either a 2, 4, or 8 character string, depending on
  119.  whether the actual value of the parameter is representable as a
  120.                           1 byte value (ShortInt, Byte)
  121.                           2 byte value (Integer, Word)
  122.                           4 byte value (LongInt)
  123.  Note that a negative value will always be returned as an 8 character
  124.  string.}
  125.  
  126. {**********************************************************************}
  127.  
  128. Function Pmod(x, modulus : LongInt) : LongInt;
  129. {Returns the mod as a positive number, regardless of the sign of X.
  130.  Recall that, e.g., -1 is congruent to (modulus-1). Thus, for example,
  131.  Pmod(-2, 7) will return 5 as the function value.}
  132.  
  133. {**********************************************************************}
  134.  
  135.   Procedure RepAll(S1, FS, SS : string; var S2 : string);
  136.   {In string S1 replace all occurrences of FS with SS, giving S2}
  137.  
  138.   function RepAllF(S1, FS, SS : string) : string;
  139.  
  140. {**********************************************************************}
  141.  
  142.   Procedure DelAll(S1, DS : string; var S2 : string);
  143.   {In string S1 delete all occurrences of DS, giving S2}
  144.  
  145.   function DelAllF(S1, DS : string) : string;
  146.  
  147. {**********************************************************************}
  148.  
  149. function PosSet(A : CharSet; S : string) : byte;
  150. {Returns the position of the first occurrance of any member of A in S}
  151.  
  152. {**********************************************************************}
  153.  
  154.   Procedure GetNext(var S1, S2 : String);
  155.   {Extracts the next substring from S1 delimited by a member of DelimSet
  156.   and returns it in S2. S1 is returned with the sub-string stripped off.
  157.   If S1 is empty on entry, both S1 and S2 will be empty on return.}
  158.  
  159.   function GetNextF(var S1 : string) : string;
  160.  
  161. {**********************************************************************}
  162.  
  163.  
  164. function UniqueFileName(Path : string; AddExt : boolean) : string;
  165. {Returns a file name which will be unique in the directory specified
  166.  by PATH. On return, the file name will be appended to PATH. If AddExt
  167.  is TRUE, an extension of .$$$ will be appended, else only the file name
  168.  will be returned.}
  169.  
  170. {**********************************************************************}
  171.  
  172.  
  173. Implementation
  174. {------------}
  175.  
  176. var
  177.   Regs : Registers;
  178.   XY   : WindowCoordinates;
  179.  
  180. {**********************************************************}
  181.  
  182. function BetwS(Lower, Item, Upper  : LongInt) : boolean;
  183. {Performs a SIGNED test of the condition that Lower <= Item <= Upper,
  184.  returning TRUE if and only if the condition is met. Lower, Item, and
  185.  Upper can be any combination of 1, 2, and 4-byte entities.}
  186.   begin
  187.     BetwS := (Item >= Lower) and (Item <= Upper);
  188.     end;
  189.  
  190. {**********************************************************}
  191.  
  192. function BetwU(Lower, Item, Upper  : LongInt) : boolean;
  193. {Performs an UNSIGNED test of the condition that Lower <= Item <= Upper,
  194.  returning TRUE if and only if the condition is met. Lower, Item, and
  195.  Upper can be any combination of 1, 2, and 4-byte entities.}
  196.   const
  197.   {In the following table, columns represent hi-word states,
  198.    rows represent lo-word states.
  199.  
  200.       1. a < b, b < c     4. a = b, b < c     7. a > b, b < c
  201.       2.        b = c     5.        b = c     8.        b = c
  202.       3.        b > c     6.        b > c     9.        b > c }
  203.  
  204.     ST  : array[1..9,1..9] of boolean =
  205.       ((  true,  true, false,  true,  true, false, false, false, false),
  206.        (  true,  true, false,  true,  true, false, false, false, false),
  207.        (  true, false, false,  true, false, false, false, false, false),
  208.        (  true,  true, false,  true,  true, false, false, false, false),
  209.        (  true,  true, false,  true,  true, false, false, false, false),
  210.        (  true, false, false,  true, false, false, false, false, false),
  211.        (  true,  true, false, false, false, false, false, false, false),
  212.        (  true,  true, false, false, false, false, false, false, false),
  213.        (  true, false, false, false, false, false, false, false, false));
  214.  
  215.   type
  216.     WO  = ( HW, LW );
  217.     X   = record
  218.             case byte of
  219.               1 : (L : LongInt);
  220.               2 : (W : array[ WO ] of word);
  221.               end;
  222.     LT  = 1..3;
  223.   var
  224.     HiState,
  225.     LoState   : byte;
  226.   function LEG(A, B : word) : LT;
  227.   {Returns 1, 2, 3 as A is <, =, > B}
  228.     begin
  229.       if A < B then
  230.         LEG := 1
  231.       else if A = B then
  232.           LEG := 2
  233.         else
  234.           LEG := 3;
  235.       end;
  236.   begin
  237.     HiState := (3 * LEG(X(Lower).W[HW], X(Item).W[HW]) - 2) +
  238.                (LEG(X(Item).W[HW], X(Upper).W[HW]) - 1);
  239.     LoState := (3 * LEG(X(Lower).W[LW], X(Item).W[LW]) - 2) +
  240.                (LEG(X(Item).W[LW], X(Upper).W[LW]) - 1);
  241.     BetwU := ST[HiState, LoState];
  242.     end;
  243.  
  244. {**********************************************************}
  245.  
  246. Function StarString;
  247. {StarString is a Boolean function which returns True if a pattern
  248.  string possibly containing one or more '*' wild cards matches a
  249.  target. It works by repeatedly extracting maximum length sub-
  250.  strings not containing a * from Pattern, determining if that sub-
  251.  string exists in Target, and, if so, deleting from Target the first
  252.  character through the end of the partial pattern. A final test is
  253.  made on the residual portion of each to determine the final truth
  254.  value of the function. Character wild cards ('?') are handled by
  255.  substituting characters 1-for-1 from the target string into the
  256.  earliest possible match and proceeding as if they were non-existant.
  257.  The function will terminate as soon as the truth value can be
  258.  determined, so that no time is wasted in execution.}
  259.   var
  260.     Index   : Byte;
  261.     TrialB  : String;
  262.  
  263.   procedure ReplQ(var Pattern1 : String; Target1 : String);
  264.   {Replaces all occurrences of '?' in Pattern1 with the corresponding
  265.    character from Target1. If Target1[0] < Pattern1[0], any '?' occurring
  266.    in the tail will not be effected.}
  267.     var
  268.       T1 : Byte;
  269.     begin
  270.       T1 := Pos('?', Pattern1);
  271.       While (T1 <> 0) and (T1 <= Byte(Pattern1[0])) do begin
  272.         Pattern1[T1] := Target1[T1];
  273.         T1 := Pos('?', Pattern1);
  274.         end;
  275.       end; {ReplQ}
  276.  
  277.   procedure Split(Instr : String; Ch : Char; var Before, After : String;
  278.                   var Index : Byte);
  279.   {Splits Instr on the first occurrence of the character Ch. The products
  280.    of the split are returned in Before and After. Ch itself is discarded.
  281.    Index returns the character position in Instr at which the split
  282.    occurred. (0 means no split)}
  283.     begin
  284.      Index := Pos(Ch, Instr);
  285.      Before := Copy(Instr, 1, Index - 1);
  286.      Delete(Instr, 1, Index);
  287.      After := Instr;
  288.      end; {Split}
  289.  
  290.   procedure CountOccur(PatStr, InStr : String; var Count : Byte);
  291.   {Counts the number of occurrences of PatStr in Instr and returns the
  292.    count in Count}
  293.     var
  294.       T1  : Byte;
  295.     begin
  296.       Count := 0;
  297.       T1 := Pos(PatStr, InStr);
  298.       While T1 <> 0 do begin
  299.         Inc(Count);
  300.         Delete(Instr, 1, T1);
  301.         T1 := Pos(PatStr, Instr);
  302.         end;
  303.       end; {CountOccur}
  304.  
  305.   procedure BuildMatch(var Pattern1, Target1 : String; var Index1 : Byte);
  306.   {If possible, constructs the version of Pattern1 which matches the
  307.    earliest substring of Target1 by eliminating character wild cards.
  308.    The position is returned in Index1}
  309.     var
  310.       Pat1  : String;
  311.       T1,           {Pointer within Target1 to start of trial match }
  312.       T2,           {FOR loop index for character replacement       }
  313.       T3,           {Number of character wild cards in Pat1         }
  314.       T4    : Byte; {Position of the T3th character wild card       }
  315.     begin
  316.       If Pattern1 = '' then exit;
  317.       If Pos('?', Pattern1) = 0 then begin
  318.         Index1 := Pos(Pattern1, Target1);
  319.         exit;
  320.         end;
  321.       T1 := 0;
  322.       Pat1 := Pattern1;
  323.       CountOccur('?', Pat1, T3);
  324.       Index1 := Pos(Pat1, Target1);
  325.       While ((T1 + Byte(Pat1[0])) <= Byte(Target1[0])) and
  326.              (Index1 = 0) do begin
  327.         For T2 := 1 to T3 do begin
  328.           T4 := Pos('?',Pat1);
  329.           Pat1[T4] := Target1[T1+T4];
  330.           end; {For}
  331.         Index1 := Pos(Pat1, Target1);
  332.         If Index1 = 0 then
  333.           Pat1 := Pattern1
  334.         else
  335.           Pattern1 := Pat1;
  336.         Inc(T1);
  337.         end; {While}
  338.       end; {BuildMatch}
  339.  
  340.   begin {StarString}
  341.  
  342.     {First, take care of all the special cases}
  343.  
  344.     While Pos('**', Pattern) <> 0 do
  345.       Delete(Pattern, Pos('**', Pattern), 1);
  346.  
  347.     If (Byte(Pattern[0]) = 0) or           {No pattern string  }
  348.        (Byte( Target[0]) = 0) then begin   {or no target string}
  349.       StarString := False;
  350.       Exit;
  351.       end;
  352.  
  353.     If Pattern[1] = '?' then
  354.       Pattern[1] := Target[1];
  355.  
  356.     If Pos('*', Pattern) = 0 then begin    {No wild cards, so }
  357.       ReplQ(Pattern, Target);              {Quick result known}
  358.       StarString := (Pattern = Target);
  359.       Exit;
  360.       end;
  361.  
  362.     Split(Pattern, '*', TrialB, Pattern, Index);
  363.     BuildMatch(TrialB, Target, Index);
  364.     If Index <> 1 then begin               {No match possible }
  365.       StarString := False;
  366.       exit;
  367.       end;
  368.  
  369.     {End of special cases. Proceed with normal processing}
  370.  
  371.     Pattern := TrialB + '*' + Pattern;     {Possible match, so  }
  372.                                            {reconstruct Pattern }
  373.                                            {and proceed         }
  374.  
  375.     While (Pos('*', Pattern) <> 0) do begin  {Still more wild cards}
  376.       Split(Pattern, '*', TrialB, Pattern, Index);
  377.                                              {Disect the pattern   }
  378.  
  379.       {TrialB now contains that portion to the left of the wildcard,
  380.        and Pattern contains what was to the right. The wild card
  381.        itself has been discarded.}
  382.  
  383.       {From TrialB build the best possible match to Target, getting
  384.        rid of character wild cards. Put the expanded string back into
  385.        TrialB for further processing.}
  386.  
  387.       BuildMatch(TrialB, Target, Index);     {Try to find a match  }
  388.                                              { and set the Index   }
  389.  
  390.       If Index = 0 then begin                {No match is possible }
  391.         StarString := False;
  392.         exit;
  393.         end
  394.       else begin                              {Still possible match}
  395.         Delete(Target, 1, Index + Byte(TrialB[0]) - 1);
  396.         end;                                  {Strip off past the  }
  397.       end; {While}                            { last left pattern  }
  398.                                               { and try again      }
  399.       If Byte(Pattern[0]) = 0 then     {'*' as last character of Pattern}
  400.         StarString := True             { so we know there is a match.   }
  401.  
  402.       else begin        { Make sure we are looking at *last* occurrance }
  403.                         {                          of Pattern in Target }
  404.         Index := Pos(Pattern, Target);
  405.         TrialB := Target;                     { Save the current target }
  406.         While Index <> 0 do begin
  407.           Delete(Target, 1, Index + Byte(Pattern[0]) - 1);
  408.                                         { Delete through end of Pattern }
  409.           Index := Pos(Pattern, Target);
  410.           If Index <> 0 then TrialB := Target;    { Save the new target }
  411.           end;
  412.  
  413.         { TrialB now contains the maximum length substring of Target    }
  414.         { which contains the *last* occurrance of Pattern.              }
  415.  
  416.         BuildMatch(Pattern, TrialB, Index);
  417.         If Index = 0 then
  418.           StarString := False
  419.         else
  420.           StarString := ((Index + Byte(Pattern[0]) - 1) = Byte(TrialB[0]));
  421.         end;
  422.     end; {Function StarString}
  423.  
  424. {***************************************************************}
  425.  
  426. function WhoAmI;
  427. var
  428.   s, o  : integer;
  429.   c     : string;
  430. begin
  431.   s := memw[PrefixSeg:$2c];    {the segment address of the start of   }
  432.   o := 0;                      { the environment area at PrefixSeg:$2c}
  433.   while memw[s:o] <> 0 do      {search for end of environment         }
  434.     o := succ(o);              {  which is marked by two 0 bytes      }
  435.   o := o + 4;                  {skip across word count       }
  436.   c := '';
  437.   repeat
  438.     c := c + chr(mem[s:o]);    {transfer fully qualified path       }
  439.     o := succ(o);              {  as a legitimate TurboPASCAL string}
  440.     until mem[s:o] = 0;
  441.   WhoAmI := c;
  442.   end;
  443.  
  444. {**********************************************************************}
  445.  
  446. function searchenvironment;
  447.   var
  448.    x,y   : integer;
  449.    cs    : string;
  450.   begin
  451.    x := memw[prefixseg:$2C];
  452.    y := 0;
  453.    while memw[x:y] <> 0 do begin
  454.     if chr(mem[x:y]) = code[1] then begin
  455.      cs := '';
  456.      repeat                           {copy up to the '='}
  457.       cs := cs + chr(mem[x:y]);
  458.       y := y + 1
  459.       until chr(mem[x:y]) = '=';
  460.      if cs = code then begin          {got a match, so}
  461.       y := y + 1;                       {space across the '='}
  462.       cs := '';
  463.       repeat                            {and copy what's on the other side}
  464.        cs := cs + chr(mem[x:y]);
  465.        y := y + 1
  466.        until mem[x:y] = 0;
  467.       searchenvironment := cs;          {and that's the function value..}
  468.       exit                              {so set it and bail out}
  469.       end {if cs = code}
  470.      end {chr(mem[x:y]) = code[1]}
  471.     else                               {no match, so}
  472.      repeat                            {just find the end of the string}
  473.       y := y + 1
  474.       until mem[x:y] = 0;
  475.     y := y + 1;                      {space across string delimiter}
  476.     end; {while}
  477.     searchenvironment := '';
  478.    end; {of searchenvironment}
  479.  
  480. {**********************************************************}
  481.  
  482. Function LoWord;
  483.   type
  484.     XT = array[1..2] of Word;
  485.   var
  486.     X : XT absolute LI;
  487.   begin
  488.     LoWord := X[1];
  489.     end;
  490.  
  491. {**********************************************************************}
  492.  
  493. Function HiWord;
  494.   type
  495.     XT = array[1..2] of Word;
  496.   var
  497.     X : XT absolute LI;
  498.   begin
  499.     HiWord := X[2];
  500.     end;
  501.  
  502. {**********************************************************************}
  503.  
  504. Function LI;
  505. {Converts two Word vbls to a LongInt}
  506. type
  507.   LItype = record
  508.              case Integer of
  509.                1 : (IT : array[1..2] of Integer);
  510.                2 : (LIT: LongInt);
  511.              end;
  512. var
  513.   X : LItype;
  514. begin
  515.   X.IT[1] := Ilo;
  516.   X.IT[2] := Ihi;
  517.   LI := X.LIT;
  518.   end;
  519.  
  520. {**********************************************************************}
  521.  
  522. Function HEX;
  523.   Type
  524.     HexByte = record
  525.                 case Byte of
  526.                   1 : (LI : LongInt);
  527.                   2 : (BY : array[0..3] of Byte);
  528.                   3 : (Ts : array[0..1] of Word);
  529.                 end;
  530.   Const
  531.     B : Array[0..15] of Char =
  532.              ('0','1','2','3','4','5','6','7','8','9',
  533.               'A','B','C','D','E','F');
  534.   Var
  535.     S1 : String;
  536.     T1,
  537.     T2 : Byte;
  538.     HB : HexByte absolute A;
  539.   Begin
  540.     Case HB.Ts[1] of
  541.       0 :  begin
  542.              T2 := 1;           {At most 2 byte vbl}
  543.              Case HB.BY[1] of
  544.                0 : T2 := 0;     {It's a Byte}
  545.                end;
  546.              end;
  547.       else T2 := 3;
  548.       end;
  549.     S1 := '';
  550.     For T1 := T2 downto 0 do
  551.       S1 := S1 + B[HB.BY[T1] shr 4] + B[HB.BY[T1] and $0F];
  552.     HEX := S1;
  553.     end;
  554.  
  555. {**********************************************************************}
  556.  
  557. function Pmod;
  558. begin
  559.   Pmod := ((x mod modulus) + modulus) mod modulus;
  560.   end;
  561.  
  562. {**********************************************************}
  563.  
  564.   Procedure RepAll(S1, FS, SS : string; var S2 : string);
  565.   {In string S1 replace all occurrences of FS with SS}
  566.     var
  567.       T1 : Integer;
  568.       S3  : string;
  569.     begin
  570.       S2 := '';
  571.       while Pos(FS, S1) <> 0 do begin
  572.         T1 := Pos(FS, S1);
  573.         S2 := S2 + copy(S1, 1, pred(T1)) + SS;
  574.         delete(S1, 1, pred(T1) + Length(FS));
  575.         end; {while}
  576.       S2 := S2 + S1;
  577.       end; {RepAll}
  578.  
  579.   function RepAllF(S1, FS, SS : string) : string;
  580.     var
  581.       S2  : string;
  582.     begin
  583.       RepAll(S1, FS, SS, S2);
  584.       RepAllF := S2;
  585.       end; {RepAllF}
  586.  
  587. {**********************************************************}
  588.  
  589.   Procedure DelAll(S1, DS : string; var S2 : string);
  590.   {In string S1 delete all occurrences of DS}
  591.     begin
  592.       RepAll(S1, DS, '', S2);
  593.       end;
  594.  
  595.   function DelAllF(S1, DS : string) : string;
  596.     begin
  597.       DelAllF := RepAllF(S1, DS, '');
  598.       end; {DelAllF}
  599.  
  600. {**********************************************************}
  601.  
  602. function PosSet(A : CharSet; S : string) : byte;
  603.   var
  604.     T1  : byte;
  605.   begin
  606.     T1 := 1;
  607.     while (not (S[T1] in A)) and (T1 < Length(S)) do
  608.       inc(T1);
  609.     if S[T1] in A then
  610.       PosSet := T1
  611.     else
  612.       PosSet := 0;
  613.     end; {PosSet}
  614.  
  615.   function TrimLeadSet(S : string; CS : CharSet) : string;
  616.     var
  617.       L : byte;
  618.     begin
  619.       L := 1;
  620.       while (S[L] in CS) and (L <= byte(S[0])) do
  621.         inc(L);
  622.       if L = 0 then
  623.         TrimLeadSet := ''
  624.       else
  625.         TrimLeadSet := Copy(S, L, 255);
  626.       end; {TrimLeadSet}
  627.  
  628.   function TrimTrailSet(S : string; CS : CharSet) : string;
  629.     begin
  630.       while (S[byte(S[0])] in CS) and (byte(S[0]) > 0) do
  631.         dec(S[0]);
  632.       TrimTrailSet := S;
  633.       end; {TrimTrailSet}
  634.  
  635.   function TrimSet(S : string; CS : CharSet) : string;
  636.     begin
  637.       TrimSet := TrimTrailSet(TrimLeadSet(S, CS), CS);
  638.       end; {TrimSet}
  639.  
  640.   Procedure GetNext(var S1, S2 : String);
  641.   {Extracts the next space-delimited string from S1 and returns it
  642.   in S2. S1 is returned with the sub-string stripped off.
  643.   If S1 is empty on entry, both S1 and S2 will be empty on return.}
  644.  
  645.   var
  646.     T1 : Integer;
  647.   begin {GetNext}
  648.     If Length(S1) = 0 then begin
  649.       S2[0] := chr(0);
  650.       Exit
  651.       end;
  652.     S1 := TrimSet(S1, DelimSet);     {Strip leading and trailing blanks}
  653.     If Length(S1) = 0 then
  654.       S2[0] := chr(0)
  655.     else
  656.       If PosSet(DelimSet, S1) <> 0 then begin
  657.         T1 := PosSet(DelimSet, S1);
  658.         S2 := Copy(S1, 1, Pred(T1));
  659.         S1 := Copy(S1, T1, Length(S1) - Pred(T1));
  660.         end
  661.       else begin
  662.         S2 := S1;
  663.         S1 := '';
  664.         end;
  665.     end; {GetNext}
  666.  
  667.   function GetNextF(var S1 : string) : string;
  668.   var
  669.     S2 : string;
  670.   begin
  671.     GetNext(S1, S2);
  672.     GetNextF := S2;
  673.     end; {GetNextF}
  674.  
  675. {**********************************************************}
  676.  
  677.  
  678. function UniqueFileName(Path : string; AddExt : boolean) : string;
  679.   var
  680.     FN :  record
  681.             case integer of
  682.               1 : (LI : LongInt);
  683.               2 : (WD : array[1..2] of word);
  684.               end;
  685.     R  :  Registers;
  686.     S  :  string;
  687.  
  688.   begin
  689.     R.AH := $2C;
  690.     MsDos(R);
  691.     FN.WD[1] := R.CX;
  692.     FN.WD[2] := R.DX;
  693.     repeat
  694.       Inc(FN.LI);
  695.       S := Path + HexL(FN.LI);
  696.       if AddExt then S := S + '.$$$';
  697.       until not ExistFile(S);
  698.     UniqueFileName := S
  699.     end;
  700.  
  701.  
  702.  
  703.  
  704. {**********************************************************}
  705.  
  706. begin {Initialization section}
  707.   StartingMode := Mem[0:$449];
  708.   With Regs do begin
  709.     AH := 8;
  710.     Intr( $10, Regs );
  711.     StartingAttr := AH;
  712.     end;
  713.   end.
  714.